#ifndef TEMP_CTRL_MODEL_HPP
#define TEMP_CTRL_MODEL_HPP

struct TemperatureControlPlan
{
	// 0 means off
	// 1 means open loop (set pwm directly)
	// 2 closed loop
	int tempFeedbackMode = 0;
	// indicate output mode
	// 0 means neutral, 1 means heat, -1 means cool
	// can be used with tempFeedbackMode == 0
	int tempCtrlMode = 0;
	float tempCtrlRampRate = 0;
	float ChangeTempSpeed = 0.5;
	int flagStartTempChange = 0;
	// as instructed, not simu-rate corrected
	unsigned long targetHoldDurMs = 0;
	// simu rate considered
	unsigned long targetHoldStartMs = 0;
};

struct TemperatureController {
	int direction = 1;
	virtual void restart(clock_t time) {};
	virtual uint8_t calculate(float targetTemp, float currentTemp, clock_t time, bool verbose = false) {
		return 0;
	};
	virtual clock_t getTime() {};
	virtual void putTime(clock_t time) {};
	// carry over the integrated error
	virtual void carryOver(TemperatureController* curve) {};

};

struct BangBangController : TemperatureController
{
	// from low to high
	const static int curvePtNum = 5;
	float ptTempDiffC[curvePtNum];
	uint8_t ptDrivePercent[curvePtNum];

	void restart(clock_t time) {};
	uint8_t calculate(float targetTemp, float currentTemp, clock_t time, bool verbose = false);
	bool update(long* curve, int curveLen);
};

struct PIDController : TemperatureController {
	float timeNormalier2Sec = 1000;
	float lastReading = 37;
	clock_t lastTime = 0;
	float kp = 10, ki = 1, kd = 0;

	float lastError = 0;
	float cumulatedError = 0;
	float errorForgetRatePerSec = 1;
	float lastTemp = 0;
	void restart(clock_t time);
	clock_t getTime();
	void putTime(clock_t time);
	uint8_t calculate(float targetTemp, float currentTemp, clock_t time, bool verbose = false);
};

struct ThermalModel
{
	// determined by heat dissipation and resistance
	//float plateToHolderSteadyStateDiff = 1;
	// we may keep the plate significantly higher than lid and expect them to converge
	// and this determines the convergence point
	float plateToHolderHeatCapacityRatio = 0.4;

	float getPleteMeanTemp(float plate, float leftplate, float rightplate);
	float getBottomPleteTemp( float leftplate, float rightplate);
	float getMeanTemp(float plate, float holder);
};

struct MainTempRecord {
	const static int RECORD_LEN = 60;
	float temps[RECORD_LEN];
	int lastReadingSecond = 0;
	uint8_t filledPointCount = 0;
	uint8_t fillingPointIdx = 0;
	float steadyRange = 1;
	int steadyDurSec = 30;
	void restart();
	bool update(float val);
	bool isSteady(float currentReading);
};
#endif